/******************************************************************************************************************
 * File: SecurityFireMonitor.java
 * Course: 17655
 * Project: Assignment A3
 * Versions:
 *	1.0 March 2011 - RAA
 *          base
 *
 * Description:
 *    Monitors security and fire
 *
 *  WAIT 15 secs before triggering sprinkler (Alarm is immediate)
 *
 * Internal Methods:
 *
 *
 ******************************************************************************************************************/
package Monitors;

import InstrumentationPackage.*;
import EventPackage.*;
import java.util.*;
import util.FileConfigReader.MonitorModeEnum;

class SecurityFireMonitor extends Thread {

    /**
     * @return the time_to_live
     */
    public static int getTime_to_live() {
        return time_to_live;
    }

    /**
     * @param aTime_to_live the time_to_live to set
     */
    public static void setTime_to_live(int aTime_to_live) {
        time_to_live = aTime_to_live;
    }

    /**
     * @return the retry_interval
     */
    public static int getRetry_interval() {
        return retry_interval;
    }

    /**
     * @param aRetry_interval the retry_interval to set
     */
    public static void setRetry_interval(int aRetry_interval) {
        retry_interval = aRetry_interval;
    }

    /**
     * @return the isSecurityAlarmSounding
     */
    public static boolean isIsSecurityAlarmSounding() {
        return isSecurityAlarmSounding;
    }

    /**
     * @param aIsSecurityAlarmSounding the isSecurityAlarmSounding to set
     */
    public static void setIsSecurityAlarmSounding(boolean aIsSecurityAlarmSounding) {
        isSecurityAlarmSounding = aIsSecurityAlarmSounding;
    }

    /**
     * @return the isFireAlarmSounding
     */
    public static boolean isIsFireAlarmSounding() {
        return isFireAlarmSounding;
    }

    /**
     * @param aIsFireAlarmSounding the isFireAlarmSounding to set
     */
    public static void setIsFireAlarmSounding(boolean aIsFireAlarmSounding) {
        isFireAlarmSounding = aIsFireAlarmSounding;
    }

    /**
     * @return the isSprinklerOn
     */
    public static boolean isIsSprinklerOn() {
        return isSprinklerOn;
    }

    /**
     * @param aIsSprinklerOn the isSprinklerOn to set
     */
    public static void setIsSprinklerOn(boolean aIsSprinklerOn) {
        isSprinklerOn = aIsSprinklerOn;
    }

    /**
     * @return the mode
     */
    public static MonitorModeEnum getMode() {
        return mode;
    }

    /**
     * @param aMode the mode to set
     */
    public static void setMode(MonitorModeEnum aMode) {
        mode = aMode;
    }
    private EventManagerInterface em = null;        // Interface object to the event manager
    private String EvtMgrIP = null;			// Event Manager IP address
    boolean Registered = true;			// Signifies that this class is registered with an event manager.
    MessageWindow mw = null;			// This is the message window
    private boolean isSecurityArmed = true;
    private Indicator securityWindowSensorIndicator;
    private Indicator securityDoorSensorIndicator;
    private Indicator securityMotionSensorIndicator;
    private Indicator fireSensorIndicator;
    private boolean bSprinklerCountDown = false;    // we are already in Sprinkler countdown
    private Long fireDetectionTime;                 // the time when the fire was detected
    // (to track the 15 secs before Sprinkler is activated)
    private static int time_to_live;
    private static int retry_interval;
    private static boolean isSecurityAlarmSounding = false;
    private static boolean isFireAlarmSounding = false;
    private static boolean isSprinklerOn = false;
    // Security or Fire+Security Mode
    private static MonitorModeEnum mode;

    public SecurityFireMonitor() {
        // event manager is on the local system

        try {
            // Here we create an event manager interface object. This assumes
            // that the event manager is on the local machine

            em = new EventManagerInterface(getRetry_interval(), getTime_to_live());

        } catch (Exception e) {
            System.out.println("ECSMonitor::Error instantiating event manager interface: " + e);
            Registered = false;

        } // catch

    } //Constructor

    public SecurityFireMonitor(String EvmIpAddress) {
        // event manager is not on the local system

        EvtMgrIP = EvmIpAddress;

        try {
            // Here we create an event manager interface object. This assumes
            // that the event manager is NOT on the local machine

            em = new EventManagerInterface(EvtMgrIP, getRetry_interval(), getTime_to_live());
        } catch (Exception e) {
            System.out.println("SecurityFireMonitor::Error instantiating event manager interface: " + e);
            Registered = false;

        } // catch

    } // Constructor

    public void run() {
        Event Evt = null;			// Event object
        EventQueue eq = null;			// Message Queue
        int EvtId = 0;				// User specified event ID

        int Delay = 1000;			// The loop delay (1 second)

        boolean Done = false;			// Loop termination flag

        boolean ON = true;                      // Used to turn on security and fire alarms
        boolean OFF = false;			// Used to turn off security and fire alarms

        boolean bWindowBreakDetected = false;
        boolean bDoorBreakDetected = false;
        boolean bMotionDetected = false;

        // Whenever a fire detection event is sent by the sensors
        boolean bFireDetected = false;

        if (em != null) {
            // Now we create the ECS status and message panel
            // Note that we set up two indicators that are initially yellow. This is
            // because we do not know if the temperature/humidity is high/low.
            // This panel is placed in the upper left hand corner and the status
            // indicators are placed directly to the right, one on top of the other

            mw = new MessageWindow("== Integrated Monitoring Console ==", 0, 0);
            setSecurityDoorSensorIndicator(new Indicator("NO DOOR INTRUSION DETECTED", mw.GetX() + mw.Width(), 0));
            setSecurityMotionSensorIndicator(new Indicator("NO MOTION INTRUSION DETECTED", mw.GetX() + mw.Width(), mw.Height()));
            setSecurityWindowSensorIndicator(new Indicator("NO WINDOW INTRUSION DETECTED", mw.GetX() + mw.Width(), 2 * mw.Height()));

            em.setMessageWindow(mw);
            
            if (mode == MonitorModeEnum.FIRE_SECURITY) {
                setFireSensorIndicator(new Indicator("NO FIRE DETECTED", mw.GetX() + mw.Width(), 3 * mw.Height()));
            }

            mw.WriteMessage("Registered with the event manager.");

            try {
                mw.WriteMessage("   Participant id: " + em.GetMyId());
                mw.WriteMessage("   Registration Time: " + em.GetRegistrationTime());

            } // try
            catch (Exception e) {
                System.out.println("Error:: " + e);

            } // catch

            /********************************************************************
             ** Here we start the main simulation loop
             *********************************************************************/
            while (!Done) {
                // Here we get our event queue from the event manager

                try {
                    eq = em.GetEventQueue();

                } // try
                catch (Exception e) {
                    mw.WriteMessage("Error getting event queue::" + e);

                } // catch

                // If there are messages in the queue, we read through them.
                // We are looking for EventIDs = -6 or -7. Event IDs of -6 are security events
                // from the security sensor; event IDs of -7 are fire detection events
                // from fire sensor.

                int qlen = eq.GetSize();

                for (int i = 0; i < qlen; i++) {
                    Evt = eq.GetEvent();

                    if (Evt.GetEventId() == -6) // Security event
                    {
                        if (Evt.GetMessage().equals("WINDOW")) {
                            bWindowBreakDetected = true;
                        } else if (Evt.GetMessage().equals("DOOR")) {
                            bDoorBreakDetected = true;
                        } else if (Evt.GetMessage().equals("MOTION")) {
                            bMotionDetected = true;
                        }
                    } else if (Evt.GetEventId() == -7) // Fire event
                    {
                        bFireDetected = true;
                    }


                    // If the event ID == 99 then this is a signal that the simulation
                    // is to end. At this point, the loop termination flag is set to
                    // true and this process unregisters from the event manager.

                    if (Evt.GetEventId() == 99) {
                        Done = true;

                        try {
                            em.UnRegister();

                        } catch (Exception e) {
                            mw.WriteMessage("Error unregistering: " + e);
                        }

                        mw.WriteMessage("\n\nSimulation Stopped. \n");

                        // Get rid of the indicators. The message panel is left for the
                        // user to exit so they can see the last message posted.

                        getSecurityDoorSensorIndicator().dispose();
                        getSecurityMotionSensorIndicator().dispose();
                        getSecurityWindowSensorIndicator().dispose();

                        if (mode == MonitorModeEnum.FIRE_SECURITY) {
                            getFireSensorIndicator().dispose();
                        }
                    } // if

                } // for

                if (bDoorBreakDetected) {

                    if (isSecurityArmed) {
                        mw.WriteMessage("Door break detected \n");
                        getSecurityDoorSensorIndicator().SetLampColorAndMessage("DOOR BREAK DETECTED", 3);
                        SecurityAlarm("DOOR", true);
                    }
                    bDoorBreakDetected = false;
                }

                if (bWindowBreakDetected) {

                    if (isSecurityArmed) {
                        mw.WriteMessage("Window break detected \n");
                        getSecurityWindowSensorIndicator().SetLampColorAndMessage("WINDOW BREAK DETECTED", 3);
                        SecurityAlarm("WINDOW", true);
                    }
                    bWindowBreakDetected = false;
                }

                if (bMotionDetected) {

                    if (isSecurityArmed) {
                        mw.WriteMessage("Motion detected \n");
                        getSecurityMotionSensorIndicator().SetLampColorAndMessage("MOTION DETECTED", 3);
                        SecurityAlarm("MOTION", true);
                    }
                    bMotionDetected = false;
                }


                if (mode == MonitorModeEnum.FIRE_SECURITY) {
                    if (bFireDetected) {
                        mw.WriteMessage("Fire detected \n");
                        getFireSensorIndicator().SetLampColorAndMessage("FIRE!!!", 3);

                        FireAlarm(true);
                        isFireAlarmSounding = true;

                        // if the fire has just been detected, display to the user the option to
                        // cancel the Sprinkler activation
                        if (!isbSprinklerCountDown()) {
                            fireDetectionTime = new Date().getTime();
                            setbSprinklerCountDown(true);
                            System.out.println("!!! Warning: Fire detected !!!");
                            System.out.println("*** The Sprinkler will be activated in 15 secs ***");
                            System.out.println("<<<Press 'C' to cancel activation or 'S' to activate now>>>\n");
                        }
                        bFireDetected = false;
                    }
                }


                // If the monitor is in Sprinkler countdown, then check the fire event time
                // if > 15 secs have elapsed, send activation to sprinkler
                // else update warnign with time
                if (isbSprinklerCountDown()) {

                    long temp = new Date().getTime();
                    //System.out.println( temp - fireDetectionTime);
               
                    if ((temp - fireDetectionTime) >= 15000) {
                        Sprinkle(true);
                        isSprinklerOn = true;
                        setbSprinklerCountDown(false);
                        System.out.println("Activating SPRINKLER");
                        SecurityFireConsole.printMenu();
                    }
                    /*
                    else {
                    System.out.println("The Sprinkler will be activated in " + (15 - ((new Date().getTime() - fireDetectionTime) * 1000)) + " secs");
                    System.out.println("Press 'C' to cancel activation or 'S' to activate Sprinkler now");
                    }
                     * */
                }


                // This delay slows down the sample rate to Delay milliseconds
                try {
                    Thread.sleep(Delay);
                } // try
                catch (Exception e) {
                    System.out.println("Sleep error:: " + e);

                } // catch

            } // while
        } else {

            System.out.println("Unable to register with the event manager.\n\n");
        } // if
    } // main

    /***************************************************************************
     * CONCRETE METHOD:: IsRegistered
     * Purpose: This method returns the registered status
     *
     * Arguments: none
     *
     * Returns: boolean true if registered, false if not registered
     *
     * Exceptions: None
     *
     ***************************************************************************/
    public boolean IsRegistered() {
        return (Registered);

    } // SetTemperatureRange

    /***************************************************************************
     * CONCRETE METHOD:: Halt
     * Purpose: This method posts an event that stops the environmental control
     *		   system.
     *
     * Arguments: none
     *
     * Returns: none
     *
     * Exceptions: Posting to event manager exception
     *
     ***************************************************************************/
    public void Halt() {
        mw.WriteMessage("***HALT MESSAGE RECEIVED - SHUTTING DOWN SYSTEM***");

        // Here we create the stop event.

        Event evt;

        evt = new Event((int) 99, "XXX");

        // Here we send the event to the event manager.

        try {
            em.SendEvent(evt);
        } // try
        catch (Exception e) {
            System.out.println("Error sending halt message:: " + e);

        } // catch

    } // Halt

    /***************************************************************************
     * CONCRETE METHOD:: SecurityAlarm
     * Purpose: This method posts events that will signal the security alarm
     *		   controller to turn on/off the alarm
     *
     * Arguments:
     *      type : WINDOW | DOOR | MOTION - the alarm to activate
     *      boolean ON(true)/OFF(false) - indicates whether to turn the
     *			 alarm on or off.
     *
     * Returns: none
     *
     * Exceptions: Posting to event manager exception
     *
     ***************************************************************************/
    public void SecurityAlarm(String type, boolean ON) {
        // Here we create the event.

        Event evt;
        if (ON) {
            evt = new Event((int) 6, type + "1");
        } else {

            evt = new Event((int) 6, type + "0");
        } // if
        // Here we send the event to the event manager.

        try {
            em.SendEvent(evt);
            isSecurityAlarmSounding = ON;
        } // try
        catch (Exception e) {
            System.out.println("Error sending security alarm control message:: " + e);
        } // catch

    }

    /***************************************************************************
     * CONCRETE METHOD:: FireAlarm
     * Purpose: This method posts events that will signal the fire alarm
     *		   controller to turn on/off the alarm
     *
     * Arguments: boolean ON(true)/OFF(false) - indicates whether to turn the
     *			 alarm on or off.
     *
     * Returns: none
     *
     * Exceptions: Posting to event manager exception
     *
     ***************************************************************************/
    public void FireAlarm(boolean ON) {
        // Here we create the event.

        Event evt;
        if (ON) {
            evt = new Event((int) 8, "FIREALARM1");
        } else {

            evt = new Event((int) 8, "FIREALARM0");
        } // if
        // Here we send the event to the event manager.

        try {
            em.SendEvent(evt);
            isFireAlarmSounding = ON;
        } // try
        catch (Exception e) {
            System.out.println("Error sending fire alarm control message:: " + e);
        } // catch

    }

    /***************************************************************************
     * CONCRETE METHOD:: FireAlarm
     * Purpose: This method posts events that will signal the fire alarm
     *		   controller to turn on/off the alarm
     *
     * Arguments: boolean ON(true)/OFF(false) - indicates whether to turn the
     *			 alarm on or off.
     *
     * Returns: none
     *
     * Exceptions: Posting to event manager exception
     *
     ***************************************************************************/
    public void Sprinkle(boolean ON) {
        // Here we create the event.

        Event evt;
        if (ON) {
            evt = new Event((int) 7, "SPRINKLER1");
        } else {

            evt = new Event((int) 7, "SPRINKLER0");
        } // if
        // Here we send the event to the event manager.

        try {
            em.SendEvent(evt);
            isSprinklerOn = ON;
        } // try
        catch (Exception e) {
            System.out.println("Error sending sprinkler control message:: " + e);
        } // catch

    }

    /**
     * @return the isSecurityArmed
     */
    public boolean isIsSecurityArmed() {
        return isSecurityArmed;

    }

    /**
     * @param isSecurityArmed the isSecurityArmed to set
     */
    public void setIsSecurityArmed(boolean isSecurityArmed) {
        this.isSecurityArmed = isSecurityArmed;

    }

    /**
     * @return the bSprinklerCountDown
     */
    public boolean isbSprinklerCountDown() {
        return bSprinklerCountDown;
    }

    /**
     * @param bSprinklerCountDown the bSprinklerCountDown to set
     */
    public void setbSprinklerCountDown(boolean bSprinklerCountDown) {
        this.bSprinklerCountDown = bSprinklerCountDown;
    }

    /**
     * @return the securityWindowSensorIndicator
     */
    public Indicator getSecurityWindowSensorIndicator() {
        return securityWindowSensorIndicator;
    }

    /**
     * @param securityWindowSensorIndicator the securityWindowSensorIndicator to set
     */
    public void setSecurityWindowSensorIndicator(Indicator securityWindowSensorIndicator) {
        this.securityWindowSensorIndicator = securityWindowSensorIndicator;
    }

    /**
     * @return the securityDoorSensorIndicator
     */
    public Indicator getSecurityDoorSensorIndicator() {
        return securityDoorSensorIndicator;
    }

    /**
     * @param securityDoorSensorIndicator the securityDoorSensorIndicator to set
     */
    public void setSecurityDoorSensorIndicator(Indicator securityDoorSensorIndicator) {
        this.securityDoorSensorIndicator = securityDoorSensorIndicator;
    }

    /**
     * @return the securityMotionSensorIndicator
     */
    public Indicator getSecurityMotionSensorIndicator() {
        return securityMotionSensorIndicator;
    }

    /**
     * @param securityMotionSensorIndicator the securityMotionSensorIndicator to set
     */
    public void setSecurityMotionSensorIndicator(Indicator securityMotionSensorIndicator) {
        this.securityMotionSensorIndicator = securityMotionSensorIndicator;
    }

    /**
     * @return the fireSensorIndicator
     */
    public Indicator getFireSensorIndicator() {
        return fireSensorIndicator;
    }

    /**
     * @param fireSensorIndicator the fireSensorIndicator to set
     */
    public void setFireSensorIndicator(Indicator fireSensorIndicator) {
        this.fireSensorIndicator = fireSensorIndicator;
    }
} // SecurityFireMonitor

